package lol.clann.minecraft.springboot.plugin.bukkit.impl;

import lol.clann.minecraft.springboot.api.annotation.compatibility.CompatibilityChecked;
import lol.clann.minecraft.springboot.api.bukkit.utils.ChunkUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.EntityUtils;
import lol.clann.minecraft.springboot.api.model.reflect.FieldInvoker;
import lol.clann.minecraft.springboot.bukkit.utils.NMSClasses;
import lol.clann.minecraft.springboot.bukkit.utils.NMSReflectUtils;
import org.bukkit.Chunk;
import org.bukkit.craftbukkit.v1_7_R4.CraftChunk;
import org.bukkit.entity.Entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Predicate;

/**
 *
 * @author pyz
 * @date 2019/5/27 11:34 PM
 */
@Service
@CompatibilityChecked
public class ChunkUtilsImpl_all implements ChunkUtils {

    @Autowired
    private EntityUtils entityUtils;
    private FieldInvoker entitySlices;

    @PostConstruct
    protected void init() {
//        这里不得不用反射,各个核心自己擅自更改这个字段的类型,和bukkit定义的不一样
        entitySlices = new FieldInvoker(NMSReflectUtils.getField(NMSClasses.getChunk(), "entitySlices"));
    }

    @Override
    public <T> T toNMS(Chunk chunk) {
        if (chunk == null) {
            return null;
        }
        return (T) ((CraftChunk) chunk).getHandle();
    }

    /**
     * 禁止修改返回的集合
     *
     * @param chunk
     * @return
     */
    @Override
    public <T> Collection<T> getTileEntities(Chunk chunk) {
        if (chunk == null) {
            return null;
        }
        net.minecraft.server.v1_7_R4.Chunk c = toNMS(chunk);
        return Collections.unmodifiableCollection(c.tileEntities.values());
    }

    @Override
    public int countEntities(Chunk chunk) {
        int size = 0;
        for (Collection entitySlice : getEntitySlices(chunk)) {
            size += entitySlice.size();
        }
        return size;
    }

    @Override
    public int countEntities(Chunk chunk, Predicate<Entity> filter) {
        return countNMSEntities(chunk, nms -> filter == null || filter.test(entityUtils.toBukkit(nms)));
    }

    @Override
    public int countNMSEntities(Chunk chunk, Predicate<Object> filter) {
        int size = 0;
        for (Collection entitySlice : getEntitySlices(chunk)) {
            for (Object o : entitySlice) {
                if (filter == null || filter.test(o)) {
                    size++;
                }
            }
        }
        return size;
    }

    @Override
    public List<Entity> getEntities(Chunk chunk) {
        return getEntities(chunk, nms -> true);
    }

    @Override
    public List<Entity> getEntities(Chunk chunk, Predicate<Entity> filter) {
        List<Entity> list = new ArrayList();
        collectEntitiy(chunk, filter, list);
        return list;
    }

    @Override
    public <T> List<T> getNMSEntities(Chunk chunk) {
        return getNMSEntities(chunk, nms -> true);
    }

    @Override
    public <T> List<T> getNMSEntities(Chunk chunk, Predicate filter) {
        List<T> list = new ArrayList();
        collectNMSEntitiy(chunk, filter, list);
        return list;
    }

    @Override
    public void collectEntitiy(Chunk chunk, Predicate<Entity> filter, Collection<Entity> conatiner) {
        for (Collection entitySlice : getEntitySlices(chunk)) {
            for (Object o : entitySlice) {
                if (filter == null) {
                    conatiner.add(entityUtils.toBukkit(o));
                } else {
                    Entity b = entityUtils.toBukkit(o);
                    if (filter.test(b)) {
                        conatiner.add(b);
                    }
                }
            }
        }
    }

    @Override
    public void collectNMSEntitiy(Chunk chunk, Predicate filter, Collection conatiner) {
        for (Collection entitySlice : getEntitySlices(chunk)) {
            for (Object o : entitySlice) {
                if (filter == null || filter.test(o)) {
                    conatiner.add(o);
                }
            }
        }
    }

    protected Iterable<Collection> getEntitySlices(Chunk chunk) {
        Object[] entitySlices = (Object[]) this.entitySlices.get(toNMS(chunk));
        return (Iterable) Arrays.asList(entitySlices);
    }
}
